package com.itaming.lycheeframework.security.authentication;

import com.itaming.lycheeframework.security.exception.InvalidTokenException;
import lombok.Getter;
import lombok.extern.slf4j.Slf4j;
import org.springframework.core.ResolvableType;
import org.springframework.util.Assert;

import java.util.Set;

/**
 * 认证上下文
 *
 * @author A.Ming
 */
@Getter
@Slf4j
public class AuthContext<T, E> {

    /**
     * 用户信息
     */
    private final E user;

    /**
     * 权限列表
     */
    private final Set<String> permissions;

    /**
     * 角色列表
     */
    private final Set<String> roles;

    /**
     * 是否为管理员
     */
    private final boolean isAdmin;

    /**
     * 是否为超级管理员
     */
    private final boolean isSuperAdmin;

    /**
     * 创建对象
     *
     * @param userId   载荷
     * @param provider 认证上下文提供者
     * @return {@link AuthContext}
     */
    static <T, E> AuthContext<T, E> of(Object userId, AuthContextProvider<T, E> provider) {
        return new AuthContext<>(userId, provider);
    }

    /**
     * 构造函数
     *
     * @param userId   载荷
     * @param provider 认证上下文提供者
     */
    @SuppressWarnings({"unchecked"})
    private AuthContext(Object userId, AuthContextProvider<T, E> provider) {
        // 检查
        Assert.notNull(userId, "User id must not be null");
        Assert.notNull(provider, "AuthContextProvider must not be null");

        // 检查类型是否一致
        Class<?> idClass = null;
        ResolvableType resolvableType = ResolvableType.forInstance(provider);
        ResolvableType[] interfaces = resolvableType.getInterfaces();
        for (ResolvableType anInterface : interfaces) {
            if (anInterface.getRawClass() == AuthContextProvider.class) {
                idClass = anInterface.getGeneric(0).resolve();
                break;
            }
        }

        if (idClass != null && !idClass.isInstance(userId)) {
            log.error("User id type mismatch: AuthContextProvider expects '{}', but the token contains '{}'"
                , idClass.getName()
                , userId.getClass().getName());
            throw new InvalidTokenException();
        }

        // 初始化信息
        user = provider.getUser((T) userId);
        permissions = provider.getPermissions((T) userId);
        roles = provider.getRoles((T) userId);
        isAdmin = provider.isAdmin(user, roles);
        isSuperAdmin = provider.isSuperAdmin(user, roles);
    }

}
